1//////////////////////////////////////////////////////////////////////
  2// LibFile: screw_drive.scad
  3//   Masks for Phillips, Torx and square (Robertson) driver holes.
  4// Includes:
  5//   include <BOSL2/std.scad>
  6//   include <BOSL2/screw_drive.scad>
  7// FileGroup: Threaded Parts
  8// FileSummary: Masks for Phillips, Torx and square (Robertson) driver holes.
  9//////////////////////////////////////////////////////////////////////
 10
 11
 12include <structs.scad>
 13
 14// Section: Phillips Drive
 15
 16// Module: phillips_mask()
 17// Synopsis: Creates a mask for a Philips screw drive.
 18// SynTags: Geom
 19// Topics: Screws, Masks
 20// See Also: hex_drive_mask(), phillips_depth(), phillips_diam(), torx_mask(), robertson_mask()
 21// Usage:
 22//   phillips_mask(size) [ATTACHMENTS];
 23// Description:
 24//   Creates a mask for creating a Phillips drive recess given the Phillips size.  Each mask can
 25//   be lowered to different depths to create different sizes of recess.  
 26// Arguments:
 27//   size = The size of the bit as an integer or string.  "#0", "#1", "#2", "#3", or "#4"
 28//   ---
 29//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
 30//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
 31//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
 32// Example:
 33//   xdistribute(10) {
 34//      phillips_mask(size="#1");
 35//      phillips_mask(size="#2");
 36//      phillips_mask(size=3);
 37//      phillips_mask(size=4);
 38//   }
 39
 40// Specs for phillips recess here:
 41//   https://www.fasteners.eu/tech-info/ISO/4757/
 42
 43function _phillips_shaft(x) = [3,4.5,6,8,10][x];
 44function _ph_bot_angle() = 28.0;
 45function _ph_side_angle() = 26.5;
 46
 47module phillips_mask(size="#2", $fn=36, anchor=BOTTOM, spin=0, orient=UP) {
 48    dummy = assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]));
 49    num = is_num(size) ? size : ord(size[1]) - ord("0");
 50    shaft = _phillips_shaft(num);
 51    b =     [0.61, 0.97, 1.47, 2.41, 3.48][num];
 52    e =     [0.31, 0.435, 0.815, 2.005, 2.415][num];
 53    g =     [0.81, 1.27, 2.29, 3.81, 5.08][num];
 54    alpha = [ 136,  138,  140,  146,  153][num];
 55    beta  = [7.00, 7.00, 5.75, 5.75, 7.00][num];
 56    gamma = 92.0;
 57    h1 = adj_ang_to_opp(g/2, _ph_bot_angle());   // height of the small conical tip
 58    h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle());   // height of larger cone
 59    l = h1+h2;
 60    h3 = adj_ang_to_opp(b/2, _ph_bot_angle());   // height where cutout starts
 61    p0 = [0,0];
 62    p1 = [adj_ang_to_opp(e/2, 90-alpha/2), -e/2];
 63    p2 = p1 + [adj_ang_to_opp((shaft-e)/2, 90-gamma/2),-(shaft-e)/2];
 64    attachable(anchor,spin,orient, d=shaft, l=l) {
 65        down(l/2) {
 66            difference() {
 67                rotate_extrude()
 68                    polygon([[0,0],[g/2,h1],[shaft/2,l],[0,l]]);
 69                zrot(45)
 70                zrot_copies(n=4, r=b/2) {                   
 71                    up(h3) {
 72                        yrot(beta) {
 73                            down(1)
 74                            linear_extrude(height=l+2, convexity=4, center=false) {
 75                                path = [p0, p1, p2, [p2.x,-p2.y], [p1.x,-p1.y]];
 76                                polygon(path);
 77                            }
 78                        }
 79                    }
 80                }
 81            }
 82        }
 83        children();
 84    }
 85}
 86
 87
 88
 89// Function: phillips_depth()
 90// Synopsis: Returns the depth a phillips recess needs to be for a given diameter.
 91// Topics: Screws, Masks
 92// See Also: phillips_mask(), hex_drive_mask(), phillips_depth(), phillips_diam(), torx_mask()
 93// Usage:
 94//   depth = phillips_depth(size, d);
 95// Description:
 96//   Returns the depth of the Phillips recess required to produce the specified diameter, or
 97//   undef if not possible.
 98// Arguments:
 99//   size = size as a number or text string like "#2"
100//   d = desired diameter
101function phillips_depth(size, d) =
102    assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
103    let(
104        num = is_num(size) ? size : ord(size[1]) - ord("0"),
105        shaft = [3,4.5,6,8,10][num],
106        g =     [0.81, 1.27, 2.29, 3.81, 5.08][num],
107        h1 = adj_ang_to_opp(g/2, _ph_bot_angle()),   // height of the small conical tip
108        h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle())   // height of larger cone
109    )
110    d>=shaft || d<g ? undef :
111    (d-g) / 2 / tan(_ph_side_angle()) + h1;
112
113
114// Function: phillips_diam()
115// Synopsis: Returns the diameter of a phillips recess of a given depth.
116// Topics: Screws, Masks
117// See Also: phillips_mask(), hex_drive_mask(), phillips_depth(), phillips_diam(), torx_mask()
118// Usage:
119//   diam = phillips_diam(size, depth);
120// Description:
121//   Returns the diameter at the top of the Phillips recess when constructed at the specified depth,
122//   or undef if that depth is not valid.  
123// Arguments:
124//   size = size as number or text string like "#2"
125//   depth = depth of recess to find the diameter of
126function phillips_diam(size, depth) =
127    assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
128    let(
129        num = is_num(size) ? size : ord(size[1]) - ord("0"),
130        shaft = _phillips_shaft(num),
131        g =     [0.81, 1.27, 2.29, 3.81, 5.08][num],
132        h1 = adj_ang_to_opp(g/2, _ph_bot_angle()),   // height of the small conical tip
133        h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle())   // height of larger cone
134    )
135    depth<h1 || depth>= h1+h2 ? undef :
136    2 * tan(_ph_side_angle())*(depth-h1) + g;
137
138
139// Section: Hex drive
140
141// Module: hex_drive_mask()
142// Synopsis: Creates a mask for a hex drive recess.
143// SynTags: Geom
144// Topics: Screws, Masks
145// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), robertson_mask()
146// Usage:
147//   hex_drive_mask(size, length, [anchor], [spin], [orient], [$slop]) [ATTACHMENTS];
148// Description:
149//   Creates a mask for hex drive.  Note that the hex recess specs requires
150//   a slightly oversized recess.  You can use $slop to increase the size by 
151//   `2 * $slop` if necessary.  
152// 
153module hex_drive_mask(size,length,l,h,height,anchor,spin,orient)
154{
155   length = one_defined([length,height,l,h],"length,height,l,h");
156   realsize = 1.0072*size + 0.0341 + 2 * get_slop();  // Formula emperically determined from ISO standard
157   linear_sweep(height=length,hexagon(id=realsize),anchor=anchor,spin=spin,orient=orient) children();
158}
159function hex_drive_mask(size,length,l,h,height,anchor,spin,orient) = no_function("hex_drive_mask");
160
161
162// Section: Torx Drive
163
164// Module: torx_mask()
165// Synopsis: Creates a mask for a torx drive recess.
166// SynTags: Geom
167// Topics: Screws, Masks
168// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), robertson_mask()
169// Usage:
170//   torx_mask(size, l, [center]) [ATTACHMENTS];
171// Description: Creates a torx bit tip.  The anchors are located on the circumscribing cylinder.  See {{torx_info()}} for allowed sizes.
172// Arguments:
173//   size = Torx size.
174//   l = Length of bit.
175//   center = If true, centers mask vertically.
176//   ---
177//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: `CENTER`
178//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
179//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
180// Examples:
181//   torx_mask(size=30, l=10, $fa=1, $fs=1);
182module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
183    od = torx_diam(size);
184    anchor = get_anchor(anchor, center, BOT, BOT);
185    attachable(anchor,spin,orient, d=od, l=l) {
186        linear_extrude(height=l, convexity=4, center=true) {
187            torx_mask2d(size);
188        }
189        children();
190    }
191}
192
193
194// Module: torx_mask2d()
195// Synopsis: Creates the 2D cross section for a torx drive recess.
196// SynTags: Geom
197// Topics: Screws, Masks
198// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), torx_info(), robertson_mask()
199// Usage:
200//   torx_mask2d(size);
201// Description: Creates a torx bit 2D profile.  The anchors are located on the circumscribing circle.   See {{torx_info()}} for allowed sizes.
202// Arguments:
203//   size = Torx size.
204// Example(2D):
205//   torx_mask2d(size=30, $fa=1, $fs=1);
206module torx_mask2d(size,anchor=CENTER,spin) {
207    info = torx_info(size);
208    od = info[0];
209    id = info[1];
210    tip = info[3];
211    rounding = info[4];
212    base = od - 2*tip;
213    $fn = quantup(segs(od/2),12);
214    attachable(anchor,spin,two_d=true,d=od){
215        difference() {
216            union() {
217                circle(d=base);
218                zrot_copies(n=2) {
219                    hull() {
220                        zrot_copies(n=3) {
221                            translate([base/2,0,0]) {
222                                circle(r=tip, $fn=$fn/2);
223                            }
224                        }
225                    }
226                }
227            }
228            zrot_copies(n=6) {
229                zrot(180/6) {
230                    translate([id/2+rounding,0,0]) {
231                        circle(r=rounding);
232                    }
233                }
234            }
235        }
236        children();
237    }
238}
239
240
241// Function: torx_info()
242// Synopsis: Returns the dimensions of a torx drive.
243// Topics: Screws, Masks
244// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), torx_info()
245// Usage:
246//   info = torx_info(size);
247// Description:
248//   Get the typical dimensional info for a given Torx size.
249//   Returns a list containing, in order:
250//   - Outer Diameter
251//   - Inner Diameter
252//   - Drive Hole Depth
253//   - External Tip Rounding Radius
254//   - Inner Rounding Radius
255// .
256//   The allowed torx sizes are:
257//   1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 27, 30, 40, 45, 50, 55,
258//   60, 70, 80, 90, 100.
259// Arguments:
260//   size = Torx size.
261function torx_info(size) =
262    let( 
263        info_arr = [      // Depth is from metric socket head screws, ISO 14583
264            //T#     OD     ID     H        Re     Ri
265            [  1, [  0.90,  0.65,  0.40,  0.059, 0.201]],  // depth interpolated
266            [  2, [  1.00,  0.73,  0.44,  0.069, 0.224]],  // depth interpolated
267            [  3, [  1.20,  0.87,  0.53,  0.081, 0.266]],  // depth interpolated
268            [  4, [  1.35,  0.98,  0.59,  0.090, 0.308]],  // depth interpolated
269            [  5, [  1.48,  1.08,  0.65,  0.109, 0.330]],  // depth interpolated
270            [  6, [  1.75,  1.27,  0.775, 0.132, 0.383]],
271            [  7, [  2.08,  1.50,  0.886, 0.161, 0.446]],  // depth interpolated
272            [  8, [  2.40,  1.75,  1.0,   0.190, 0.510]],
273            [  9, [  2.58,  1.87,  1.078, 0.207, 0.554]],  // depth interpolated
274            [ 10, [  2.80,  2.05,  1.142, 0.229, 0.598]],
275            [ 15, [  3.35,  2.40,  1.2,   0.267, 0.716]],  // depth interpolated
276            [ 20, [  3.95,  2.85,  1.4,   0.305, 0.859]],  // depth interpolated
277            [ 25, [  4.50,  3.25,  1.61,  0.375, 0.920]],  
278            [ 27, [  5.07,  3.65,  1.84,  0.390, 1.108]],
279            [ 30, [  5.60,  4.05,  2.22,  0.451, 1.194]],
280            [ 40, [  6.75,  4.85,  2.63,  0.546, 1.428]],
281            [ 45, [  7.93,  5.64,  3.115, 0.574, 1.796]],
282            [ 50, [  8.95,  6.45,  3.82,  0.775, 1.816]],
283            [ 55, [ 11.35,  8.05,  5.015, 0.867, 2.667]],
284            [ 60, [ 13.45,  9.60,  5.805, 1.067, 2.883]],
285            [ 70, [ 15.70, 11.20,  6.815, 1.194, 3.477]],
286            [ 80, [ 17.75, 12.80,  7.75,  1.526, 3.627]],
287            [ 90, [ 20.20, 14.40,  8.945, 1.530, 4.468]],
288            [100, [ 22.40, 16.00, 10.79,  1.720, 4.925]],
289        ],
290        found = struct_val(info_arr,size)
291    )
292    assert(found, str("Unsupported Torx size, ",size))
293    found;
294
295
296// Function: torx_diam()
297// Synopsis: Returns the diameter of a torx drive.
298// Topics: Screws, Masks
299// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), torx_info()
300// Usage:
301//   diam = torx_diam(size);
302// Description: Get the typical outer diameter of Torx profile.
303// Arguments:
304//   size = Torx size.
305function torx_diam(size) = torx_info(size)[0];
306
307
308// Function: torx_depth()
309// Synopsis: Returns the typical depth of a torx drive recess.
310// Topics: Screws, Masks
311// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), torx_info()
312// Usage:
313//   depth = torx_depth(size);
314// Description: Gets typical drive hole depth.
315// Arguments:
316//   size = Torx size.
317function torx_depth(size) = torx_info(size)[2];
318
319
320
321// Section: Robertson/Square Drives
322
323// Module: robertson_mask()
324// Synopsis: Creates a mask for a Robertson/Square drive recess.
325// SynTags: Geom
326// Topics: Screws, Masks
327// See Also: phillips_mask(), hex_drive_mask(), torx_mask(),  phillips_depth(), phillips_diam(), torx_info(), robertson_mask()
328// Usage:
329//   robertson_mask(size, [extra], [ang], [$slop=]);
330// Description:
331//   Creates a mask for creating a Robertson/Square drive recess given the drive size as an integer.
332//   The width of the recess will be oversized by `2 * $slop`.  Note that this model is based
333//   on an incomplete spec.   https://www.aspenfasteners.com/content/pdf/square_drive_specification.pdf
334//   We determined the angle by doing print tests on a Prusa MK3S with $slop set to 0.05.
335// Arguments:
336//   size = The size of the square drive, as an integer from 0 to 4.
337//   extra = Extra length of drive mask to create.
338//   ang = taper angle of each face.  Default: 2.5
339//   ---
340//   $slop = enlarge recess by this twice amount.  Default: 0
341//   anchor = Translate so anchor point is at origin (0,0,0).  See [anchor](attachments.scad#subsection-anchor).  Default: TOP
342//   spin = Rotate this many degrees around the Z axis after anchor.  See [spin](attachments.scad#subsection-spin).  Default: `0`
343//   orient = Vector to rotate top towards, after spin.  See [orient](attachments.scad#subsection-orient).  Default: `UP`
344// Side Effects:
345//   Sets tag to "remove" if no tag is set.  
346// Example:
347//   robertson_mask(size=2);
348// Example:
349//   difference() {
350//       cyl(d1=2, d2=8, h=4, anchor=TOP);
351//       robertson_mask(size=2);
352//   }
353module robertson_mask(size, extra=1, ang=2.5,anchor=TOP,spin,orient) {
354    dummy=assert(is_int(size) && size>=0 && size<=4);
355    Mmin = [0.0696, 0.0900, 0.1110, 0.1315, 0.1895][size];
356    Mmax = [0.0710, 0.0910, 0.1126, 0.1330, 0.1910][size];
357    M = (Mmin + Mmax) / 2 * INCH;
358    Tmin = [0.063, 0.105, 0.119, 0.155, 0.191][size];
359    Tmax = [0.073, 0.113, 0.140, 0.165, 0.201][size];
360    T = (Tmin + Tmax) / 2 * INCH;
361    Fmin = [0.032, 0.057, 0.065, 0.085, 0.090][size];
362    Fmax = [0.038, 0.065, 0.075, 0.095, 0.100][size];
363    F = (Fmin + Fmax) / 2 * INCH;
364    h = T + extra;
365    Mslop=M+2*get_slop();
366    Mtop = Mslop + 2*adj_ang_to_opp(F+extra,ang);
367    Mbot = Mslop - 2*adj_ang_to_opp(T-F,ang);
368    anchors = [named_anchor("standard",[0,0,T-h/2], UP, 0)];
369    default_tag("remove")
370      attachable(anchor,spin,orient,size=[Mbot,Mbot,T],size2=[Mtop,Mtop],anchors=anchors){
371        down(T/2)
372            intersection(){
373                prismoid([Mbot,Mbot],[Mtop,Mtop],h=h,anchor=BOT);
374                cyl(d1=0, d2=Mslop/(T-F)*sqrt(2)*h, h=h, anchor=BOT);
375            }
376        children();
377      }
378}
379
380
381
382// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap